#pragma once

#include "basechart_base.h"
#include "basechart_painter.h"

class BaseChartLayerable;
class BaseChartCustomPlot;
class S_WIDGETS_EXPORT BaseChartLayer : public QObject {
  Q_OBJECT
  /// \cond INCLUDE_QPROPERTIES
  Q_PROPERTY(BaseChartCustomPlot* parentPlot READ parentPlot)
  Q_PROPERTY(QString name READ name)
  Q_PROPERTY(int index READ index)
  Q_PROPERTY(QList<BaseChartLayerable*> children READ children)
  Q_PROPERTY(bool visible READ visible WRITE setVisible)
  Q_PROPERTY(LayerMode mode READ mode WRITE setMode)
  /// \endcond
 public:
  /*!
    Defines the different rendering modes of a layer. Depending on the mode,
    certain layers can be replotted individually, without the need to replot
    (possibly complex) layerables on other layers.

    \see setMode
  */
  enum LayerMode {
    lmLogical  ///< Layer is used only for rendering order, and shares paint
               ///< buffer with all other adjacent logical layers.
    ,
    lmBuffered  ///< Layer has its own paint buffer and may be replotted
                ///< individually (see \ref replot).
  };
  Q_ENUMS(LayerMode)

  BaseChartLayer(BaseChartCustomPlot* parentPlot, const QString& layerName);
  virtual ~BaseChartLayer();

  // getters:
  BaseChartCustomPlot* parentPlot() const { return mParentPlot; }
  QString name() const { return mName; }
  int index() const { return mIndex; }
  QList<BaseChartLayerable*> children() const { return mChildren; }
  bool visible() const { return mVisible; }
  LayerMode mode() const { return mMode; }

  // setters:
  void setVisible(bool visible);
  void setMode(LayerMode mode);

  // non-virtual methods:
  void replot();

 protected:
  // property members:
  BaseChartCustomPlot* mParentPlot;
  QString mName;
  int mIndex;
  QList<BaseChartLayerable*> mChildren;
  bool mVisible;
  LayerMode mMode;

  // non-property members:
  QWeakPointer<BaseChartAbstractPaintBuffer> mPaintBuffer;

  // non-virtual methods:
  void draw(BaseChartPainter* painter);
  void drawToPaintBuffer();
  void addChild(BaseChartLayerable* layerable, bool prepend);
  void removeChild(BaseChartLayerable* layerable);

 private:
  Q_DISABLE_COPY(BaseChartLayer)

  friend class BaseChartCustomPlot;
  friend class BaseChartLayerable;
};

class S_WIDGETS_EXPORT BaseChartLayerable : public QObject {
  Q_OBJECT
  /// \cond INCLUDE_QPROPERTIES
  Q_PROPERTY(bool visible READ visible WRITE setVisible)
  Q_PROPERTY(BaseChartCustomPlot* parentPlot READ parentPlot)
  Q_PROPERTY(BaseChartLayerable* parentLayerable READ parentLayerable)
  Q_PROPERTY(
      BaseChartLayer* layer READ layer WRITE setLayer NOTIFY layerChanged)
  Q_PROPERTY(bool antialiased READ antialiased WRITE setAntialiased)
  /// \endcond
 public:
  BaseChartLayerable(BaseChartCustomPlot* plot, QString targetLayer = QString(),
                     BaseChartLayerable* parentLayerable = nullptr);
  virtual ~BaseChartLayerable();

  // getters:
  bool visible() const { return mVisible; }
  BaseChartCustomPlot* parentPlot() const { return mParentPlot; }
  BaseChartLayerable* parentLayerable() const {
    return mParentLayerable.data();
  }
  BaseChartLayer* layer() const { return mLayer; }
  bool antialiased() const { return mAntialiased; }

  // setters:
  void setVisible(bool on);
  Q_SLOT bool setLayer(BaseChartLayer* layer);
  bool setLayer(const QString& layerName);
  void setAntialiased(bool enabled);

  // introduced virtual methods:
  virtual double selectTest(const QPointF& pos, bool onlySelectable,
                            QVariant* details = nullptr) const;

  // non-property methods:
  bool realVisibility() const;

 signals:
  void layerChanged(BaseChartLayer* newLayer);

 protected:
  // property members:
  bool mVisible;
  BaseChartCustomPlot* mParentPlot;
  QPointer<BaseChartLayerable> mParentLayerable;
  BaseChartLayer* mLayer;
  bool mAntialiased;

  // introduced virtual methods:
  virtual void parentPlotInitialized(BaseChartCustomPlot* parentPlot);
  virtual Interaction selectionCategory() const;
  virtual QRect clipRect() const;
  virtual void applyDefaultAntialiasingHint(
      BaseChartPainter* painter) const = 0;
  virtual void draw(BaseChartPainter* painter) = 0;
  // selection events:
  virtual void selectEvent(QMouseEvent* event, bool additive,
                           const QVariant& details,
                           bool* selectionStateChanged);
  virtual void deselectEvent(bool* selectionStateChanged);
  // low-level mouse events:
  virtual void mousePressEvent(QMouseEvent* event, const QVariant& details);
  virtual void mouseMoveEvent(QMouseEvent* event, const QPointF& startPos);
  virtual void mouseReleaseEvent(QMouseEvent* event, const QPointF& startPos);
  virtual void mouseDoubleClickEvent(QMouseEvent* event,
                                     const QVariant& details);
  virtual void wheelEvent(QWheelEvent* event);

  // non-property methods:
  void initializeParentPlot(BaseChartCustomPlot* parentPlot);
  void setParentLayerable(BaseChartLayerable* parentLayerable);
  bool moveToLayer(BaseChartLayer* layer, bool prepend);
  void applyAntialiasingHint(BaseChartPainter* painter, bool localAntialiased,
                             AntialiasedElement overrideElement) const;

 private:
  Q_DISABLE_COPY(BaseChartLayerable)

  friend class BaseChartCustomPlot;
  friend class BaseChartLayer;
  friend class BaseChartAxisRect;
};

Q_DECLARE_METATYPE(BaseChartLayer::LayerMode)
